﻿//Created: 3/24/2013 @ 10:20pm by Dagradt
//Last changed date: 3/24/2013 @ 10:20pm by Dagradt

using Action = Styx.TreeSharp.Action;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Routines;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Druid_Feral_
{
    public class Main : CombatRoutine
    {
        public override string Name { get { return "Druid(Feral)"; } }
        public override WoWClass Class { get { return WoWClass.Druid; } }
        LocalPlayer Me { get { return StyxWoW.Me; } }

        private Composite _preCombatBuffs, _combatBuffs, _Heal, _Combat;
        public override Composite PreCombatBuffBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _preCombatBuffs; } }
        public override Composite CombatBuffBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _combatBuffs; } }
        public override Composite HealBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _Heal; } }
        public override Composite CombatBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _Combat; } }

        #region Initialize
        public override void Initialize()
        {
            _preCombatBuffs = createPreCombatBuffs();
            _combatBuffs = createCombatBuffs();
            _Heal = createHeal();
            _Combat = createCombat();
        }
        #endregion

        #region Pre-Combat Buffs
        Composite createPreCombatBuffs()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        //0	0.00	flask,type=spring_blossoms
                        //1	0.00	food,type=sea_mist_rice_noodles
                        //2	0.00	mark_of_the_wild,if=!aura.str_agi_int.up
                        Cast(1234, ret => true),
                        //3	0.00	healing_touch,if=!buff.dream_of_cenarius_damage.up&talent.dream_of_cenarius.enabled
                        Cast(1234, ret => true),
                        //4	0.00	cat_form
                        Cast(1234, ret => !buffExists(1234, Me, true)),
                        //5	0.00	savage_roar
                        Cast(1234, ret => !buffExists(1234, Me, true))
                        //7	0.00	virmens_bite_potion
                        ));
        }
        #endregion

        #region Combat Buffs
        Composite createCombatBuffs()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        ));
        }
        #endregion

        #region Heal
        Composite createHeal()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        ));
        }
        #endregion

        #region Combat
        Composite createCombat()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        //8	1.00	auto_attack
                        //9	0.00	swap_action_list,name=aoe,if=active_enemies>=5
                        //A	0.00	skull_bash_cat
                        Cast(1234, ret => true),
                        //B	3.02	berserking
                        Cast(1234, ret => true),
                        //C	16.61	healing_touch,if=buff.predatory_swiftness.up&buff.predatory_swiftness.remains<=1.5&buff.dream_of_cenarius_damage.down
                        Cast(1234, ret => buffExists(1234, Me, true) && buffTimeLeft(1234, Me, true) <= 1500 && !buffExists(1234, Me, true)),
                        //D	4.99	savage_roar,if=buff.savage_roar.down
                        Cast(1234, ret => !buffExists(1234, Me, true)),
                        //E	0.00	faerie_fire,cycle_targets=1,if=debuff.weakened_armor.stack<3
                        Cast(1234, ret => true),
                        //F	30.56	healing_touch,if=buff.predatory_swiftness.up&combo_points>=4&buff.dream_of_cenarius_damage.stack<2
                        Cast(1234, ret => buffExists(1234, Me, true) && comboPoints >= 4 && buffStackCount(1234, Me, true) < 2),
                        //G	6.04	healing_touch,if=buff.natures_swiftness.up
                        Cast(1234, ret => buffExists(1234, Me, true)),
                        //H	7.71	use_item,name=grips_of_the_haunted_forest,sync=tigers_fury
                        //I	14.94	tigers_fury,if=(energy<=35&!buff.omen_of_clarity.react)
                        Cast(1234, ret => (Energy <= 35 && !buffExists(1234, Me, true)),
                        //J	2.93	berserk,if=buff.tigers_fury.up|(target.time_to_die<18&cooldown.tigers_fury.remains>6)
                        Cast(1234, ret => true),
                        //K	2.04	ferocious_bite,if=combo_points>=1&dot.rip.ticking&dot.rip.remains<=3&target.health.pct<=25
                        Cast(1234, ret => comboPoints >= 1 && debuffExists(1234, Me.CurrentTarget) && debuffTimeLeft(1234, Me.CurrentTarget) <= 3000 && Me.CurrentTarget.HealthPercent <= 25),
                        //L	2.95	thrash_cat,if=target.time_to_die>=6&buff.omen_of_clarity.react&dot.thrash_cat.remains<3
                        Cast(1234, ret => true),
                        //M	0.64	ferocious_bite,if=(target.time_to_die<=4&combo_points>=5)|(target.time_to_die<=1&combo_points>=3)
                        Cast(1234, ret => true),
                        //N	3.27	savage_roar,if=buff.savage_roar.remains<=3&combo_points>0&target.health.pct<25
                        Cast(1234, ret => buffTimeLeft(1234, Me, true) <= 3000 && comboPoints > 0 && Me.CurrentTarget.HealthPercent < 25),
                        //O	1.00	virmens_bite_potion,if=(combo_points>=5&target.health.pct<=25&buff.rune_of_reorigination.up&buff.dream_of_cenarius_damage.up)|target.time_to_die<=40
                        //P	0.17	natures_swiftness,if=enabled&combo_points>=5&target.health.pct<=25&buff.dream_of_cenarius_damage.down&buff.predatory_swiftness.down&buff.rune_of_reorigination.up&target.time_to_die>30
                        Cast(1234, ret => true),
                        //Q	0.67	rip,line_cd=30,if=combo_points>=5&target.health.pct<=25&buff.virmens_bite_potion.up&buff.dream_of_cenarius_damage.up&buff.rune_of_reorigination.up&target.time_to_die>30
                        Cast(1234, ret => true),
                        //R	1.10	natures_swiftness,if=enabled&buff.dream_of_cenarius_damage.down&buff.predatory_swiftness.down&combo_points>=5&target.health.pct<=25
                        Cast(1234, ret => true),
                        //S	30.02	pool_resource,wait=0.25,if=combo_points>=5&dot.rip.ticking&target.health.pct<=25&((energy<50&buff.berserk.down)|(energy<25&buff.berserk.remains>1))
                        //T	6.49	ferocious_bite,if=combo_points>=5&dot.rip.ticking&target.health.pct<=25
                        Cast(1234, ret => comboPoints >= 5 && buffExists(1234, Me.CurrentTarget) && Me.CurrentTarget.HealthPercent <= 25),
                        //U	2.79	natures_swiftness,if=combo_points>=5&buff.rune_of_reorigination.react&buff.rune_of_reorigination.remains>1
                        Cast(1234, ret => comboPoints >= 5 && buffExists(1234, Me, true) && buffTimeLeft(1234, Me, true) > 1000),
                        //V	6.88	rip,if=combo_points>=5&buff.rune_of_reorigination.react
                        Cast(1234, ret => comboPoints >= 5 && buffExists(1234, Me, true)),
                        //W	6.24	rip,if=combo_points>=5&target.time_to_die>=6&dot.rip.remains<2&buff.dream_of_cenarius_damage.up
                        Cast(1234, ret => true),
                        //X	1.98	natures_swiftness,if=buff.dream_of_cenarius_damage.down&buff.predatory_swiftness.down&combo_points>=5&dot.rip.remains<3&(buff.berserk.up|dot.rip.remains+1.9<=cooldown.tigers_fury.remains)
                        Cast(1234, ret => true),
                        //Y	3.03	rip,if=combo_points>=5&target.time_to_die>=6&dot.rip.remains<2&(buff.berserk.up|dot.rip.remains+1.9<=cooldown.tigers_fury.remains)
                        Cast(1234, ret => true),
                        //Z	1.68	savage_roar,if=buff.savage_roar.remains<=3&combo_points>0&buff.savage_roar.remains+2>dot.rip.remains
                        Cast(1234, ret => true),
                        //a	3.26	savage_roar,if=buff.savage_roar.remains<=6&combo_points>=5&buff.savage_roar.remains+2<=dot.rip.remains
                        Cast(1234, ret => buffTimeLeft(1234, Me, true) <= 6000 && comboPoints >= 5 && buffTimeLeft(1234, Me, true) + 2000 <= debuffTimeLeft(1234, Me.CurrentTarget)),
                        //b	48.82	pool_resource,wait=0.25,if=combo_points>=5&((energy<50&buff.berserk.down)|(energy<25&buff.berserk.remains>1))&dot.rip.remains>=6.5
                        //c	14.12	ferocious_bite,if=combo_points>=5&dot.rip.remains>6
                        Cast(1234, ret => comboPoints >= 5 && debuffTimeLeft(1234, Me.CurrentTarget) > 6000),
                        //d	35.04	rake,if=buff.rune_of_reorigination.react
                        Cast(1234, ret => buffExists(1234, Me, true)),
                        //e	28.84	rake,if=dot.rake.remains<9&buff.dream_of_cenarius_damage.up
                        Cast(1234, ret => debuffTimeLeft(1234, Me.CurrentTarget) < 9000 && buffExists(1234, Me, true)),
                        //f	1.75	rake,if=dot.rake.remains<3
                        Cast(1234, ret => debuffTimeLeft(1234, Me.CurrentTarget) < 3000),
                        //g	129.72	pool_resource,wait=0.25,for_next=1
                        //h	22.02	thrash_cat,if=dot.thrash_cat.remains<3&target.time_to_die>=6&(dot.rip.remains>=4|buff.berserk.up)
                        Cast(1234, ret => true),
                        //i	0.00	run_action_list,name=filler,if=buff.omen_of_clarity.react
                        //j	0.00	run_action_list,name=filler,if=(combo_points<5&dot.rip.remains<3)|(combo_points=0&buff.savage_roar.remains<2)
                        //k	0.00	run_action_list,name=filler,if=buff.predatory_swiftness.remains>1
                        //l	0.00	run_action_list,name=filler,if=target.time_to_die<=8.5
                        //m	0.00	run_action_list,name=filler,if=buff.tigers_fury.up|buff.berserk.up
                        //n	0.00	run_action_list,name=filler,if=cooldown.tigers_fury.remains<=3
                        //o	0.00	run_action_list,name=filler,if=energy.time_to_max<=1
                        ));
        }
        #endregion

        #region Methods

        #region Buff Checks
        public bool buffExists(int Buff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == Buff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == Buff);
                if (Results != null)
                    return true;
            }
            return false;
        }
        //public bool buffExists(int Buff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Buff);
        //        if (Results != null)
        //            return true;
        //    }
        //    return false;
        //}
        public double buffTimeLeft(int Buff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == Buff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == Buff);
                if (Results != null)
                {
                    if (Results.TimeLeft.TotalMilliseconds > 0)
                        return Results.TimeLeft.TotalMilliseconds;
                }
            }
            return 0;
        }
        //public double buffTimeLeft(int Buff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Buff);
        //        if (Results != null)
        //        {
        //            if (Results.TimeLeft.TotalMilliseconds > 0)
        //                return Results.TimeLeft.TotalMilliseconds;
        //        }
        //    }
        //    return 0;
        //}
        public uint buffStackCount(int Buff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == Buff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == Buff);
                if (Results != null)
                    return Results.StackCount;
            }
            return 0;
        }
        //public uint buffStackCount(int Buff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Buff);
        //        if (Results != null)
        //            return Results.StackCount;
        //    }
        //    return 0;
        //}
        #endregion

        #region Cache Checks
        public IEnumerable<WoWAura> cachedAuras = new List<WoWAura>();
        public IEnumerable<WoWAura> cachedTargetAuras = new List<WoWAura>();
        public void getCachedAuras()
        {
            if (Me.CurrentTarget != null)
                cachedTargetAuras = Me.CurrentTarget.GetAllAuras();
            cachedAuras = Me.GetAllAuras();
        }
        #endregion

        #region Cooldown Checks
        long lastUsed;
        public TimeSpan cooldownLeft(int Spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(Spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                if (Cooldowns.TryGetValue(Result, out lastUsed))
                {
                    if (DateTime.Now.Ticks < lastUsed)
                        return Result.CooldownTimeLeft;
                    return TimeSpan.MaxValue;
                }
            }
            return TimeSpan.MaxValue;
        }
        public bool onCooldown(int Spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(Spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                if (Cooldowns.TryGetValue(Result, out lastUsed))
                {
                    if (DateTime.Now.Ticks < lastUsed)
                        return Result.Cooldown;
                    return false;
                }
            }
            return false;
        }
        public TimeSpan spellCooldownLeft(int Spell, bool useTracker = false)
        {
            if (useTracker)
                return cooldownLeft(Spell);
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
            {
                if (results.Override != null)
                    return results.Override.CooldownTimeLeft;
                return results.Override.CooldownTimeLeft;
            }
            return TimeSpan.MaxValue;
        }
        public bool spellOnCooldown(int Spell, bool useTracker = false)
        {
            if (useTracker)
                return !onCooldown(Spell);
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
                return results.Override != null ? results.Override.Cooldown : results.Original.Cooldown;
            return false;
        }
        private Dictionary<WoWSpell, long> Cooldowns = new Dictionary<WoWSpell, long>();
        public void spellUsed(int spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                Cooldowns[Result] = Result.CooldownTimeLeft.Ticks + DateTime.Now.Ticks;
            }
        }
        #endregion

        #region Debuff Checks
        public bool debuffExists(int debuff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == debuff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == debuff);
                if (Results != null)
                    return true;
            }
            return false;
        }
        //public bool debuffExists(int Debuff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Debuff);
        //        if (Results != null)
        //            return true;
        //    }
        //    return false;
        //}
        public double debuffTimeLeft(int debuff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == debuff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == debuff);
                if (Results != null)
                {
                    if (Results.TimeLeft.TotalMilliseconds > 0)
                        return Results.TimeLeft.TotalMilliseconds;
                }
            }
            return 0;
        }
        //public double debuffTimeLeft(int Debuff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Debuff);
        //        if (Results != null)
        //        {
        //            if (Results.TimeLeft.TotalMilliseconds > 0)
        //                return Results.TimeLeft.TotalMilliseconds;
        //        }
        //    }
        //    return 0;
        //}
        public uint debuffStackCount(int debuff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == debuff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == debuff);
                if (Results != null)
                    return Results.StackCount;
            }
            return 0;
        }
        //public uint debuffStackCount(int debuff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(debuff);
        //        if (Results != null)
        //            return Results.StackCount;
        //    }
        //    return 0;
        //}
        #endregion

        #region Macro Checks
        public bool useCDs { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return useCDs and 0 or 1", 0)); } }
        public bool Manual { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return Manual and 0 or 1", 0)); } }
        #endregion

        #region Player Checks
        public double comboPoints
        {
            get
            {
                try
                {
                    using (StyxWoW.Memory.AcquireFrame())
                    {
                        return Styx.WoWInternals.Lua.GetReturnVal<int>("return GetComboPoints(\"player\");", 0);
                    }
                }
                catch
                {
                    return 0;
                }
            }
        }
        public int Energy
        {
            get
            {
                try
                {
                    using (StyxWoW.Memory.AcquireFrame())
                    {
                        return Styx.WoWInternals.Lua.GetReturnVal<int>("return UnitPower(\"player\");", 0);
                    }
                }
                catch
                {
                    return 0;
                }
            }
        }
        #endregion

        #region Target Checks
        public bool gotTarget { get { return Me.GotTarget && Me.CurrentTarget != null && (Me.CurrentTarget.IsAlive && !Me.CurrentTarget.IsFriendly || Me.CurrentTarget.Name == "Training Dummy"); } } //create Dummy return
        public double targetDistance(WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.DistanceSqr;
                if (Results != null)
                    return onTarget.DistanceSqr;
            }
            return 0;
        }
        #endregion

        #region Spell Casting
        private delegate T Selection<out T>(object Context);
        Composite Cast(int Spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCanCast = false)
        {
            return
                new Decorator(ret =>
                {
                    if (reqs != null && !reqs(ret))
                        return false;
                    if (!ignoreCanCast && !SpellManager.CanCast(Spell, onTarget != null ? onTarget(ret) : Me.CurrentTarget))
                        return false;
                    return true;
                },
                    new Action(ret =>
                    {
                        WoWSpell castingSpell = Me.CastingSpell;
                        if (castingSpell != null && castingSpell.Id != Spell)
                            Lua.DoString("SpellStopCasting()");
                        Logging.Write("Casting: " + Spell + " on " + Me.CurrentTarget.Name);
                        SpellManager.Cast(Spell, (onTarget != null ? onTarget(ret) : null));
                        spellUsed(Spell);
                    })
                    );
        }
        Composite dropCast(int Spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCanCast = false)
        {
            return
                new Decorator(ret =>
                {
                    if (reqs != null && !reqs(ret))
                        return false;
                    if (!ignoreCanCast && !SpellManager.CanCast(Spell, onTarget != null ? onTarget(ret) : Me.CurrentTarget))
                        return false;
                    return true;
                },
                    new Action(ret =>
                    {
                        WoWSpell castingSpell = Me.CastingSpell;
                        if (castingSpell != null && castingSpell.Id != Spell)
                            Lua.DoString("SpellStopCasting()");
                        Logging.Write("Casting: " + Spell + " on " + Me.CurrentTarget.Name);
                        SpellManager.Cast(Spell, (onTarget != null ? onTarget(ret) : null));
                        SpellManager.ClickRemoteLocation(Me.CurrentTarget.Location);
                        spellUsed(Spell);
                    })
                    );
        }
        #endregion

        #endregion

        //add channel casting and unit region and auto attack... what does Truth do? ~> Truth sets the return to the tracker or the spellmanager. Use spellmanager for spells that can reset and tracker for spells that do not reset
    }
}
